package org.artifact.core.context.bytebuf;

import org.artifact.core.util.BitUtil;

import java.util.Arrays;

public class ArrayByteBuff extends IByteBuff {
	
	/** 增长量 */
	public static final int DEFAULT_ADD_CAPACITY = 64;
	/** 初始大小 */
	public static final int DEFAULT_CAPACITY = 256;
	/** 数据 */
	private byte[] data;
	/** 每次增加长度 */
	private int addCapacity = 0;
	/** 读索引 */
	private int readIndex = 0;
	/** 写索引 */
	private int writeIndex = 0;
	
	private int limit;
	/**
	 * 注意：如果传入data数据时，initCapacity参数将失效并且把数据放入开始位置
	 * @param initCapacity 初始大小
	 * @param addCapacity	增长量
	 * @param data			数据
	 */
	private ArrayByteBuff(int initCapacity, int addCapacity, byte[] data) {
		if (data == null) {
			this.data = new byte[initCapacity];
		}else {
			this.data = data;
			this.writeIndex += (data.length);
		}
		this.addCapacity = addCapacity;
	}

	/**
	 * 默认配置构建
	 * @return
	 */
	public static ArrayByteBuff allocate() {
		return new ArrayByteBuff(DEFAULT_CAPACITY, DEFAULT_ADD_CAPACITY, null);
	}

	/**
	 * 指定初始大小构建
	 * @param initCapacity 初始大小
	 * @return
	 */
	public static ArrayByteBuff allocate(int initCapacity) {
		return new ArrayByteBuff(initCapacity, DEFAULT_ADD_CAPACITY, null);
	}

	/**
	 * 自定义大小、增长量 构建
	 * @param initCapacity 初始大小
	 * @param addCapacity	增长量
	 * @return
	 */
	public static ArrayByteBuff allocate(int initCapacity, int addCapacity) {
		return new ArrayByteBuff(initCapacity, addCapacity, null);
	}

	/**
	 * 通过已有数据构建
	 * @param data
	 * @return
	 */
	public static ArrayByteBuff wrap(byte[] data) {
		return new ArrayByteBuff(DEFAULT_CAPACITY, DEFAULT_ADD_CAPACITY, data);
	}

	/**
	 * 通过指定数据构建
	 * @param array 	数据
	 * @param offset	开始位置
	 * @param length	数据长度
	 * @return
	 */
	public static ArrayByteBuff wrap(byte[] array, int offset, int length) {
		byte[] data = new byte[length];
		System.arraycopy(array, offset, data, 0, length);
		return new ArrayByteBuff(DEFAULT_CAPACITY, DEFAULT_ADD_CAPACITY, data);
	}
	
	/**
	 * 检查长度
	 * @param l 需要的长度
	 */
	private void checkLength(int l) {
		int length = this.data.length;
		int spill = (writeIndex + l) - length;
		if (spill < addCapacity) {
			this.data = Arrays.copyOf(this.data, length + addCapacity);
		}else {
			this.data = Arrays.copyOf(this.data, length + l + (l % addCapacity));
		}
	}

	/**
	 * 重置读、写位置
	 * @return
	 */
	public void clear() {
		this.readIndex = 0;
		this.writeIndex = 0;
		this.limit = 0;
	}
	
	/**
	 * 数据大小
	 * @return
	 */
	public int size() {
		return this.data.length;
	}
	
	public boolean isRead(int l){
		if (this.readIndex + l > this.writeIndex)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "SK_ByteBuff [data=" + Arrays.toString(data) + ", addCapacity="
				+ addCapacity + ", readIndex=" + readIndex + ", writeIndex="
				+ writeIndex + "]";
	}
	
	
	
	
	@Override
	public void onWriteBytes(byte[] value) {
		checkLength(value.length);
		System.arraycopy(value, 0, this.data, writeIndex, value.length);
		writeIndex += value.length;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteByte(byte value) {
		checkLength(Byte.BYTES);
		this.data[writeIndex++] = value;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteShort(short value) {
		checkLength(Short.BYTES);
		BitUtil.putShort(this.data,writeIndex,value);
		writeIndex+=Short.BYTES;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteInt(int value) {
		checkLength(Integer.BYTES);
		BitUtil.putInt(this.data,writeIndex,value);
		writeIndex+=Integer.BYTES;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteLong(long value) {
		checkLength(Long.BYTES);
		BitUtil.putLong(this.data,writeIndex,value);
		writeIndex+=Long.BYTES;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteFloat(float value) {
		checkLength(Float.BYTES);
		BitUtil.putFloat(this.data,writeIndex,value);
		writeIndex+=Float.BYTES;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteDouble(double value) {
		checkLength(Double.BYTES);
		BitUtil.putDouble(this.data,writeIndex,value);
		writeIndex+=Double.BYTES;
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public void onWriteBoolean(boolean value) {
		checkLength(1);
		this.data[writeIndex++] = (byte) (value ? 1 : 0);
		limit = writeIndex > limit ? writeIndex : limit;
	}

	@Override
	public byte[] onReadBytes(int length) {
		byte[] b = new byte[length];
		if (!this.isRead(length)) {
			return b;
		}
		System.arraycopy(this.data, this.readIndex, b, 0, length);
		this.readIndex += length;
		return b;
	}

	@Override
	public byte onReadByte() {
		if (!this.isRead(Byte.BYTES)) {
			return 0;
		}
		return this.data[readIndex++];
	}

	@Override
	public short onReadShort() {
		short _v = 0;
		if (!this.isRead(Short.BYTES)) {
			return _v;
		}
		_v = BitUtil.getShort(this.data,readIndex);
		readIndex+=Short.BYTES;
		return _v;
	}

	@Override
	public int onReadInt() {
		int _v = 0;
		if (!this.isRead(Integer.BYTES)) {
			return _v;
		}
		_v = BitUtil.getInt(this.data,readIndex);
		readIndex+=Integer.BYTES;
		return _v;
	}

	@Override
	public long onReadLong() {
		long _v = 0;
		if (!this.isRead(Long.BYTES)) {
			return _v;
		}
		_v = BitUtil.getLong(this.data,readIndex);
		readIndex+=Long.BYTES;
		return _v;
	}

	@Override
	public float onReadFloat() {
		float _v = 0.0f;
		if (!this.isRead(Float.BYTES)) {
			return _v;
		}
		_v = BitUtil.getFloat(this.data,readIndex);
		readIndex+=Float.BYTES;
		return _v;
	}

	@Override
	public double onReadDouble() {
		double _v = 0.0d;
		if (!this.isRead(Double.BYTES)) {
			return _v;
		}
		_v = BitUtil.getDouble(this.data,readIndex);
		readIndex+=Double.BYTES;
		return _v;
	}

	@Override
	public boolean onReadBoolean() {
		if (!this.isRead(Float.BYTES)) {
			return false;
		}
		return this.data[readIndex++] == 1;
	}

	@Override
	public int getReaderIndex() {
		return this.readIndex;
	}

	@Override
	public void setReaderIndex(int readerIndex) {
		this.readIndex = readerIndex;
	}

	@Override
	public int getWriterIndex() {
		return this.writeIndex;
	}

	@Override
	public void setWriterIndex(int writerIndex) {
		this.writeIndex = writerIndex;
	}

	@Override
	public int readableBytes() {
		return writeIndex - readIndex;
	}

	@Override
	public byte getTag() {
		return this.data[readIndex];
	}

	@Override
	public void skipBytes(int length) {
		if (!isRead(length)) {
			return;
		}
		this.readIndex += length;
	}

	@Override
	public byte[] array() {
		return Arrays.copyOfRange(data, 0, limit);
	}

}
